﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Linq;
using Amib.Threading;
using HPPClientLibrary.DownLoad;
using HPPNet;
using HPPUtil;
using HPPUtil.Extensions;
using HPPUtil.Helpers;

namespace HPPClientLibrary
{
    public sealed class HPPClient : ClientBase, IDisposable
    {
        #region Fields

        private const int NUM_CONNECTION = 10;
        private static CSServer _server;

        private readonly string DAT_HASH_FILEINFO = ConfigManager.ConfigFolder.GetStringWithLastSlash() + "hash_fileInfo.dat";
        private readonly string DAT_HASH_KNOWN = ConfigManager.ConfigFolder.GetStringWithLastSlash() + "known.dat";
        private readonly string DAT_SHAREDIR = ConfigManager.ConfigFolder.GetStringWithLastSlash() + "sharedir.dat";
        private readonly string DAT_HASH_JOB = ConfigManager.ConfigFolder.GetStringWithLastSlash() + "downloadJob.dat";

        private static object _syncRoot = new object();
        private ConfigManager _config;

        private IWorkItemsGroup _hashWIG;
        private bool _isCSServerinitialized;


        #endregion

        #region Constructor

        public HPPClient()
        {
            _threadPool = new SmartThreadPool();
            _hashFileInfoDict = SerializeHelper.DeSerialize(DAT_HASH_FILEINFO) as Dictionary<string, HppFileInfo> ?? new Dictionary<string, HppFileInfo>();

            _knownDict = SerializeHelper.DeSerialize(DAT_HASH_KNOWN) as HashSet<string> ?? new HashSet<string>();


            _server = new CSServer(this, NUM_CONNECTION, Int16.MaxValue);
            this.HashCalculator = new HashCalculator();

            _shareDir = SerializeHelper.DeSerialize(DAT_SHAREDIR) as HashSet<string> ?? new HashSet<string>();

            CSServerListenPort = Config.CSServerPort;
            _isCSServerinitialized = false;
            //Init();
            //StartListen();
        }



        #endregion

        
        #region Property


        public override DownloadManager DownloadManager
        {
            get
            {
                if (_manager == null)
                {
                    lock (_syncRoot)
                    {
                        if (_manager == null)
                        {
                            _manager = new DownloadManager(this, (SerializeHelper.DeSerialize(DAT_HASH_JOB) as Dictionary<string,JobInfo> ?? new Dictionary<string, JobInfo>()));
                            return _manager;
                        }
                    }
                }
                return _manager;
            }
        }

        public override ConfigManager Config
        {
            get
            {
                if (_config == null)
                {
                    lock (_syncRoot)
                    {
                        if (_config == null)
                        {
                            _config = LoadConfig();
                            return _config;
                        }
                    }
                }
                return _config;
            }


        }

        public CSServer CSServer
        {
            get
            {
                return _server;
            }
        }

        public override IHashCalculator HashCalculator{ get; set; }

        private IWorkItemsGroup HashWIG
        {
            get
            {
                if (_hashWIG == null)
                {
                    lock (_syncRoot)
                    {
                        if (_hashWIG == null)
                        {
                            _hashWIG = this.ThreadPool.CreateWorkItemsGroup(1,
                                                                 new WIGStartInfo()
                                                                     {WorkItemPriority = WorkItemPriority.Lowest});
                            return _hashWIG;
                        }
                    }
                }

                return _hashWIG;
            }
        }



        #endregion



        #region 公共方法

        public override void Download(string hash, string fileName, bool isStartNow)
        {
            if (!Directory.Exists(Config.SaveFilePath))
            {
                Directory.CreateDirectory(Config.SaveFilePath);
            }
            DownloadManager.Download(hash, Config.SaveFilePath, fileName,isStartNow);
        }

        public void CheckNewShareDir()
        {
            List<string> toHashList = new List<string>();
            foreach (string s in _shareDir)
            {
                DirectoryInfo di = new DirectoryInfo(s);
                FileInfo[] fileInfos = di.GetFiles();

                foreach (FileInfo info in fileInfos)
                {
                    if (!_knownDict.Contains(info.FullName))
                    {
                        toHashList.Add(info.FullName);
                    }
                }

                
            }

            this.CalcFileAsync(toHashList, new Action<string, string>(AfterHashFile) );

        }

        public void ConnectServer()
        {
            string debugStr = ConfigurationManager.AppSettings["Debug"];
            if (debugStr != null && "true".Equals(debugStr.Trim().ToLower()))
            {
                string ip = ConfigurationManager.AppSettings["ServerIP"];
                string port = ConfigurationManager.AppSettings["Port"];
                CommonLogger.Logger.Debug("ServerIP================  " + ip + "  Port =========" + port);
                ConnectServer(new IPEndPoint(IPAddress.Parse(ip), int.Parse(port)));
            }
        }


        public override void StartListen()
        {
            if(_isCSServerinitialized)
            {
                _server.Start(new IPEndPoint(IPAddress.Any,_csServerListenPort));                
            }
            else
            {
                Init();
                _isCSServerinitialized = true;
                _server.Start(new IPEndPoint(IPAddress.Any, _csServerListenPort));
            }

        }

        /// <summary>
        /// 连接到中央服务器
        /// </summary>
        /// <param name="ipEndPoint"></param>
        public override void ConnectServer(IPEndPoint ipEndPoint)
        {
            UploadShareDirAsync(ipEndPoint);
        }
        /*
        /// <summary>
        /// 是否已连接到中央服务器
        /// </summary>
        public override bool Connected
        {
            get; protected set;
        }
        */
        /// <summary>
        /// 上传共享目录
        /// </summary>
        public override void UploadShareDir(IPEndPoint targetIPEndPoint)
        {
            List<ShareFileItem> itemList = new List<ShareFileItem>();

            foreach (KeyValuePair<string, HppFileInfo> dict in _hashFileInfoDict)
            {
                ShareFileItem item = new ShareFileItem(dict.Value.FullPathName, dict.Key, dict.Value.Len);

                itemList.Add(item);
            }

            ShareFilesXO shareFilesXo = new ShareFilesXO(CSServerListenPort, itemList);

            string xmlStr = shareFilesXo.ToXml().ToCompleteString();

            string url = string.Format("http://{0}:{1}/", targetIPEndPoint.Address, targetIPEndPoint.Port);

            Uri uri = new Uri(url);
            
            HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri);
            request.Method = "POST";
            request.ServicePoint.Expect100Continue = false;
            
            using (Stream requestStream = request.GetRequestStream())
            {
                byte[] bytes = Encoding.UTF8.GetBytes(xmlStr);
                requestStream.Write(bytes, 0, bytes.Length);
            }
             
            HttpWebResponse response = (HttpWebResponse) request.GetResponse();

            if (response.StatusCode == HttpStatusCode.OK)
            {
                Connected = true;
                ConnectServerEndPoint = targetIPEndPoint;
            }
            
        }

        /// <summary>
        /// 异步上传共享文件夹
        /// </summary>
        /// <param name="targetIPEndPoint">服务器IPEndPoint</param>
        public void UploadShareDirAsync(IPEndPoint targetIPEndPoint)
        {

            ThreadPool.QueueWorkItem(new Action<IPEndPoint>(UploadShareDir), targetIPEndPoint);

        }

        public void Init()
        {
#if TRACE
            try
            {


#endif
                _server.Init();//Start前一定要Init！！！
                _isCSServerinitialized = true;

#if TRACE
            }
            catch (Exception ex)
            {
                CommonLogger.Logger.Fatal("Init()", ex);
            }
#endif
        }
        #endregion 公共方法

        #region 私有方法

        private ConfigManager LoadConfig()
        {
            string configFolderPath = Directory.GetCurrentDirectory().GetStringWithLastSlash() +
                                      ConfigManager.ConfigFolder;
            if (!Directory.Exists(configFolderPath))
            {
                Directory.CreateDirectory(configFolderPath);
            }

            string configFilePath = configFolderPath + "/" + ConfigManager.ConfigFileName;
            if (File.Exists(configFilePath))
            {
                return ConfigManager.Load(configFilePath);
            }

#if DEBUG
            ConfigManager cm =  ConfigManager.Load("../../../Debug.ini");
            if (cm != null)
            {
                return cm;
            }
#endif

            return ConfigManager.Empty;
        }

        private void AfterHashFile(string fullPath, string hash)
        {
            
            
            try
            {
                long len;
                FileInfo info = new FileInfo(fullPath);
                len = info.Length;
                AddHashFileInfoDict(new KeyValuePair<string, HppFileInfo>(hash, new HppFileInfo(){FullPathName = fullPath, Len = len}));
                AddKnownDict(hash);
            }
            catch (Exception ex)
            {
                CommonLogger.Logger.Error("In AfterHashFile()", ex);
                
            }
            
        }
        
        private void CalcFileAsync(string fullpath, Action<string, string> postAction)
        {
            HashWIG.QueueWorkItem((path, action) =>
            {
                string result = HashCalculator.CalcFile(path);
                if (action != null)
                {
                    action(path, result);
                }
            }, fullpath, postAction);
        }
        
        private void CalcFileAsync(IEnumerable<string> pathList, Action<string, string> postAction)
        {
            foreach (string s in pathList)
            {
                this.CalcFileAsync(s, postAction);
            }
        }



        

        

        #endregion 私有方法

 

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposing)
        {

            if (disposing)
            {
                // release managed resources.
            }
            BeforeClose();
        }

        private void BeforeClose()
        {
            
            ThreadPool.QueueWorkItem(() => SerializeHelper.Serialize(_hashFileInfoDict, DAT_HASH_FILEINFO));
            ThreadPool.QueueWorkItem(() => SerializeHelper.Serialize(_knownDict, DAT_HASH_KNOWN));
            ThreadPool.QueueWorkItem(() => SerializeHelper.Serialize(_shareDir, DAT_SHAREDIR));
            ThreadPool.QueueWorkItem(() => SerializeHelper.Serialize(DownloadManager.HashJobInfoDict, DAT_HASH_JOB));
            ThreadPool.QueueWorkItem(
                () => Config.Save(ConfigManager.ConfigFolder.GetStringWithLastSlash() + ConfigManager.ConfigFileName));
            ThreadPool.WaitForIdle();
            CommonLogger.Logger.Info("In BeforeClose(), 全部保存完毕");
        }

        ~HPPClient()
        {
            Dispose(false);
        }
    }
}
